#ifndef __STRUCTURE_ENGINE_H__
#define __STRUCTURE_ENGINE_H__

////////////////////////////////////////////////////////////////////////////////
// Includes
////////////////////////////////////////////////////////////////////////////////

#include <vector>
#include <utility>
#include "Point3D.hpp"
#include "Vector3D.hpp"
#include "GraphicalGraph.hpp"
#include "TKMacros.h"
#include "Structures.hpp"
#include "GraphDataTypes.hpp"
#include "Triple.hpp"

////////////////////////////////////////////////////////////////////////////////
// Forward declarations
////////////////////////////////////////////////////////////////////////////////

class wxString;
class wxColour;
class wxRect;

////////////////////////////////////////////////////////////////////////////////
// Namespaces
////////////////////////////////////////////////////////////////////////////////

namespace SC {

////////////////////////////////////////////////////////////////////////////////
// Enumerations
////////////////////////////////////////////////////////////////////////////////

typedef enum FDA {
	NormalFDA = 0,
	HierarchicalFDA
};

////////////////////////////////////////////////////////////////////////////////
// Implementation
////////////////////////////////////////////////////////////////////////////////

/// Structure engine
class StructureEngine {

	public:

		/// Constructor
		StructureEngine()
		:	graph(NULL), root_idx(0), anchor(0), fd_alg(NormalFDA),
			timestep(0.1f), damping(0.1f), energy_threshold(0.1f),
			repulsion_power(1.f), attraction_power(1.f),
			enable_3D(false), usage_on_z_scale(0.f)
		{}

		/// Initialise
		void Initialise(const bool = true);

		/// Clear
		void Clear();

		/// Reset locations
		void ResetLocations();

		/// Returns the anchor
		const unsigned int GetAnchor() const { return this->anchor; }

		/// Sets the anchor
		void SetAnchor(const unsigned int);

		/// Returns the FDA in use (read-only)
		const FDA GetFDAlg() const { return this->fd_alg; }

		/// Sets the FDA in use
		void SetFDAlg(const FDA);

		/// Returns the selected systems list (read-only)
		const std::vector<int> & GetSelectedSystems() const { return this->selected_systems; }

		/// Sets the selected systems list
		void SetSelectedSystems(std::vector<int> &);

		/// Returns the interaction links (read-only)
		const std::vector< Types::Triple<unsigned int, unsigned int, float> > & GetInteractionLinks() const { return this->interaction_links; }

		/// Sets the interaction links
		std::vector< Types::Triple<unsigned int, unsigned int, float> > & InteractionLinks() { return this->interaction_links; }

		/// Sets graph
		void SetGraph(Types::GraphicalGraph<wxString,wxColour,NODE_DATA_TYPE,wxRect,ARC_DATA_TYPE> & value) {
			this->graph = &value; this->root_idx = this->anchor = 0;
		}

		/// Returns the timestep (read-only)
		const float GetTimeStep() const { return this->timestep; }

		/// Returns the timestep
		float & TimeStep() { return this->timestep; }

		/// Returns the damping (read-only)
		const float GetDamping() const { return this->damping; }

		/// Returns the damping
		float & Damping() { return this->damping; }

		/// Returns the energy threshold (read-only)
		const float GetEnergyThreshold() const { return this->energy_threshold; }

		/// Returns the energy threshold
		float & EnergyThreshold() { return this->energy_threshold; }

		/// Returns the repulsion power (read-only)
		const float GetRepulsionPower() const { return this->repulsion_power; }

		/// Returns the repulsion power
		float & RepulsionPower() { return this->repulsion_power; }

		/// Returns the attraction power (read-only)
		const float GetAttractionPower() const { return this->attraction_power; }

		/// Returns the attraction power
		float & AttractionPower() { return this->attraction_power; }

		/// Returns the 3D state (read-only)
		const float Is3DEnabled() const { return this->enable_3D; }

		/// Enables/disbales 3D
		void Enable3D(const bool);

		/// Returns the z-axis scale (read-only)
		const float GetUsageOnZScale() const { return this->usage_on_z_scale; }

		/// Sets the z-axis scale
		void SetUsageOnZScale(const float);

		/// Distributes the skeleton mass using a factor applied at each hierarchy level
		void DistributeSkeletonMass(const float);

		/// Refresh node data
		void RefreshNode(const unsigned int);

		/// Update
		const bool Update(float &);

	protected:

		/// Graph
		Types::GraphicalGraph<wxString,wxColour,NODE_DATA_TYPE,wxRect,ARC_DATA_TYPE> * graph;

		/// Graph root index
		unsigned int root_idx;

		/// Structure anchor
		unsigned int anchor;

		/// Algorithm used to process bodies locations
		FDA fd_alg;

		/// Time-step
		float timestep;

		/// Damping
		float damping;

		/// Energy threshold
		float energy_threshold;

		/// Repulsion power
		float repulsion_power;

		/// Attraction power
		float attraction_power;

		/// Enable/disable 3D
		bool enable_3D;

		/// Usage on Z-axis scale
		float usage_on_z_scale;

		/// Selected systems to consider
		std::vector<int> selected_systems;

		/// Selected state
		std::vector<bool> nodes_selected_state;

		/// Forces applied to bodies
		std::vector< Types::Vector3D<float> > forces;

		/// Interaction links
		std::vector< Types::Triple<unsigned int, unsigned int, float> > interaction_links;

		/// Graph Force-Directed Algorithm
		void fda();
		void fda_att(unsigned int, unsigned int, std::vector<unsigned int> &, std::vector< std::pair<unsigned int, unsigned int> > &);

		/// Graph Force-Directed Algorithm using hierarchy from the root to move bodies
		void hfda();

		/// HFDA main function (recursive)
		void hfda_step(const unsigned int, std::vector<unsigned int> &);

		/// Apply the given shift to the force of all sub nodes of the given node
		void shift_sub_nodes_force(const unsigned int, const Types::Vector3D<float> &, std::vector<unsigned int> &);

		/// Computes Coulomb repulsion force
		const Types::Vector3D<float> coulomb_repulsion(
			const Types::Node<NData,AData> &,
			const Types::Node<NData,AData> &
		);

		/// Computes Hooke attraction force
		const Types::Vector3D<float> hooke_attraction(
			const Types::Point3D<float> &,
			const Types::Point3D<float> &,
			const float,
			const float
		);

};

////////////////////////////////////////////////////////////////////////////////
// Closing Namespaces
////////////////////////////////////////////////////////////////////////////////

}

////////////////////////////////////////////////////////////////////////////////

#endif
